import { createRef, useEffect } from "react";
import * as BABYLON from "@babylonjs/core";
import "@babylonjs/loaders/glTF";

import "./App.css";

const load = function (scene: BABYLON.Scene): Promise<BABYLON.AbstractMesh[]> {
  return new Promise((resolve, reject) => {
    BABYLON.SceneLoader.ImportMesh(
      [
        "Cylinder",
        "Plane.001",
        "Cube",
        "Cube.002",
        "Cylinder.004",
        "Plane.002",
        "Cylinder.005",
        "Cylinder.002",
        "Cylinder.001",
      ],
      "./assets/City Pack/",
      "1994 Nissan 180MX.glb",
      scene,
      function (meshes) {
        createFollowCamera(scene, meshes[0]);

        resolve(meshes);
      }
    );
  });
};

var createFollowCamera = function (
  scene: BABYLON.Scene,
  target: BABYLON.Nullable<BABYLON.AbstractMesh>
) {
  var camera = new BABYLON.FollowCamera(
    "tankFollowCamera",
    new BABYLON.Vector3(0, 0, 0),
    scene
  );
  camera.heightOffset = 2;
  camera.rotationOffset = 180;
  camera.cameraAcceleration = 0.1;
  camera.maxCameraSpeed = 1;
  camera.lockedTarget = target;
  camera.attachControl(true);

  // camera.inputs.removeByType("FollowCameraPointersInput");

  return camera;
};

var createScene = async function (
  engine: BABYLON.WebGPUEngine,
  onCreate: (scene: BABYLON.Scene) => void
) {
  var scene = new BABYLON.Scene(engine);

  new BABYLON.AxesViewer(scene, 5);

  const localAxes = new BABYLON.AxesViewer(scene);

  var light = new BABYLON.HemisphericLight(
    "light",
    new BABYLON.Vector3(0, 1, 0),
    scene
  );
  light.intensity = 0.7;

  // Ground
  var ground = BABYLON.MeshBuilder.CreateGroundFromHeightMap(
    "ground",
    "https://www.babylonjs-playground.com/textures/heightMap.png",
    {
      width: 200,
      height: 200,
      subdivisions: 100,
      minHeight: 0,
      maxHeight: 10,
      updatable: false,
    },
    scene
  );

  var groundMaterial = new BABYLON.StandardMaterial("ground", scene);
  groundMaterial.diffuseTexture = new BABYLON.Texture(
    "https://www.babylonjs-playground.com/textures/grass.png",
    scene
  );
  (groundMaterial.diffuseTexture as BABYLON.Texture).uScale = 6;
  (groundMaterial.diffuseTexture as BABYLON.Texture).vScale = 6;
  groundMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
  ground.position.y = -2.05;
  ground.material = groundMaterial;

  // Skybox
  var skybox = BABYLON.Mesh.CreateBox("skyBox", 1000.0, scene);
  var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
  skyboxMaterial.backFaceCulling = false;
  skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture(
    "https://www.babylonjs-playground.com/textures/TropicalSunnyDay",
    scene
  );
  skyboxMaterial.reflectionTexture.coordinatesMode =
    BABYLON.Texture.SKYBOX_MODE;
  skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
  skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
  skyboxMaterial.disableLighting = true;
  skybox.material = skyboxMaterial;

  const tank = (await load(scene))[0];
  tank.position.y = 5;
  tank.rotationQuaternion = null;
  tank.rotation.y -= Math.PI;
  (tank as any).speed = 1;
  (tank as any).frontVector = new BABYLON.Vector3(0, 0, 1);

  console.log((tank as any).move);

  //set up input map
  var inputMap: Map<any, boolean> = new Map();
  scene.actionManager = new BABYLON.ActionManager(scene);
  scene.actionManager.registerAction(
    new BABYLON.ExecuteCodeAction(
      BABYLON.ActionManager.OnKeyDownTrigger,
      function (evt) {
        inputMap.set(evt.sourceEvent.key, evt.sourceEvent.type === "keydown");
      }
    )
  );
  scene.actionManager.registerAction(
    new BABYLON.ExecuteCodeAction(
      BABYLON.ActionManager.OnKeyUpTrigger,
      function (evt) {
        inputMap.set(evt.sourceEvent.key, evt.sourceEvent.type === "keydown");
      }
    )
  );

  scene.onBeforeRenderObservable.add(function () {
    if (tank) {
      if (inputMap.get("w") || inputMap.get("ArrowUp")) {
        tank.moveWithCollisions(
          (tank as any).frontVector.multiplyByFloats(
            (tank as any).speed,
            (tank as any).speed,
            (tank as any).speed
          )
        );
        console.log((tank as any).frontVector);
      }
      if (inputMap.get("a") || inputMap.get("ArrowLeft")) {
        tank.rotation.y -= 0.01;
        (tank as any).frontVector = new BABYLON.Vector3(
          Math.sin(tank.rotation.y),
          0,
          Math.cos(tank.rotation.y)
        );
        console.log((tank as any).frontVector);
      }
      if (inputMap.get("s") || inputMap.get("ArrowDown")) {
        tank.moveWithCollisions(
          (tank as any).frontVector.multiplyByFloats(
            -(tank as any).speed,
            -(tank as any).speed,
            -(tank as any).speed
          )
        );
        console.log((tank as any).frontVector);
      }
      if (inputMap.get("d") || inputMap.get("ArrowRight")) {
        tank.rotation.y += 0.01;
        (tank as any).frontVector = new BABYLON.Vector3(
          Math.sin(tank.rotation.y),
          0,
          Math.cos(tank.rotation.y)
        );
        console.log((tank as any).frontVector);
      }
    }
  });

  onCreate(scene);
};

async function init(canvas: HTMLCanvasElement) {
  const engine = new BABYLON.WebGPUEngine(canvas);
  await engine.initAsync();
  // call the createScene function
  createScene(engine, (scene) => {
    // run the render loop
    engine.runRenderLoop(function () {
      scene.render();
    });
  });

  // the canvas/window resize event handler
  window.addEventListener("resize", function () {
    engine.resize();
  });
}

function App() {
  const canvasRef = createRef<HTMLCanvasElement>();

  useEffect(() => {
    if (!canvasRef.current) {
      return;
    }

    init(canvasRef.current);
  });

  return (
    <div className="App">
      <canvas className="canvas" ref={canvasRef}></canvas>
    </div>
  );
}

export default App;
